/*
 * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kernel/os/os.h"
#include "audio/pcm/audio_pcm.h"
#include "audio/manager/audio_manager.h"
#include "kernel/os/os_queue.h"
#include "model_app.h"
#include "audio_process.h"
#include "audio_record.h"

#define RECORDER_THREAD_STACK_SIZE    (16 * 1024)
#ifndef MIN
#define  MIN(A, B)        ((A) < (B) ? (A) : (B))
#endif

#define SAMPLE_CHANNELS   4     /* we sample 4 channels as example */
#define SAMPLE_SIZE       2     /* 2 bytes each sample */
#define SAMPLES_16K_10MS  160   /* for 16 kHz */
#define AUDIO_CARD_ID   AUDIO_SND_CARD_DEFAULT

static OS_Thread_t record_thread;
static OS_Thread_t model_thread;

struct audioContext {
    struct pcm_config pcm_cfg;
    unsigned int read_len;
    void *read_data;
    short *channel_1_data;
    short *channel_2_data;
    short *channel_3_data;
    short *channel_4_data;
};

extern HAL_Status ac107_pdm_init(Audio_Device device, uint16_t volume, uint32_t sample_rate);
extern HAL_Status ac107_pdm_deinit(void);

static int _audio_record_start(struct audioContext *pAudio)
{
    int ret = 0;
    HAL_Status status;

    memset(pAudio, 0, sizeof(*pAudio));

    /* start ac107, pdm mode */
    status = ac107_pdm_init(AUDIO_IN_DEV_AMIC, 31, SAMPLE_RATE_OF_RECORD);
    if (status != HAL_OK) {
        printf("ac107 init fail.\n");
        return -1;
    }

    printf("here we record %d channels. also, we can record 1/2/4 channels\n", SAMPLE_CHANNELS);

    /* enable 4 channel */
    audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_ROUTE, AUDIO_IN_DEV_AMIC, 1);
    audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_ROUTE, AUDIO_IN_DEV_LINEIN, 1);
    audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_ROUTE, AUDIO_IN_DEV_DMIC, 1);

    /* set volume of amic and linein */
    audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_VOLUME_LEVEL, AUDIO_IN_DEV_AMIC, 5);
    audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_VOLUME_LEVEL, AUDIO_IN_DEV_LINEIN, 7);

    pAudio->pcm_cfg.channels = SAMPLE_CHANNELS;
    pAudio->pcm_cfg.format = PCM_FORMAT_S16_LE;
    pAudio->pcm_cfg.period_count = 2;
    pAudio->pcm_cfg.period_size = SAMPLES_16K_10MS;
    pAudio->pcm_cfg.rate = SAMPLE_RATE_OF_RECORD;

    ret = snd_pcm_open(AUDIO_CARD_ID, PCM_IN, &pAudio->pcm_cfg);
    if (ret != 0) {
        printf("pcm open error\n");
        goto err1;
    }

    pAudio->read_len = SAMPLES_16K_10MS * SAMPLE_SIZE * SAMPLE_CHANNELS;
    pAudio->read_data = (void *)malloc(pAudio->read_len);
    if (pAudio->read_data == NULL) {
        goto err2;
    }

    pAudio->channel_1_data = (short *)malloc(SAMPLES_16K_10MS * SAMPLE_SIZE);
    if (pAudio->channel_1_data == NULL) {
        goto err2;
    }

    pAudio->channel_2_data = (short *)malloc(SAMPLES_16K_10MS * SAMPLE_SIZE);
    if (pAudio->channel_2_data == NULL) {
        goto err2;
    }

    pAudio->channel_3_data = (short *)malloc(SAMPLES_16K_10MS * SAMPLE_SIZE);
    if (pAudio->channel_3_data == NULL) {
        goto err2;
    }

    pAudio->channel_4_data = (short *)malloc(SAMPLES_16K_10MS * SAMPLE_SIZE);
    if (pAudio->channel_4_data == NULL) {
        goto err2;
    }

    return 0;

err2:
    free(pAudio->channel_4_data);
    free(pAudio->channel_3_data);
    free(pAudio->channel_2_data);
    free(pAudio->channel_1_data);
    free(pAudio->read_data);
    snd_pcm_close(AUDIO_CARD_ID, PCM_IN);
err1:
    ac107_pdm_deinit();
    return -1;
}

static int _audio_record_get_data(struct audioContext *pAudio)
{
    int i;
    int ret;

    ret = snd_pcm_read(AUDIO_CARD_ID, pAudio->read_data, pAudio->read_len);
    if (ret != pAudio->read_len) {
        printf("snd_pcm_read fail.\n");
        return -1;
    }

    /* we separate pcm data, so we can get pcm data of each channel */
    for (i = 0; i < SAMPLES_16K_10MS; i++) {
        pAudio->channel_1_data[i] = ((short *)pAudio->read_data)[4 * i];
        pAudio->channel_2_data[i] = ((short *)pAudio->read_data)[4 * i + 1];
        pAudio->channel_3_data[i] = ((short *)pAudio->read_data)[4 * i + 2];
        pAudio->channel_4_data[i] = ((short *)pAudio->read_data)[4 * i + 3];
    }

    return 0;
}

static void _audio_record_stop(struct audioContext *pAudio)
{
    free(pAudio->channel_4_data);
    free(pAudio->channel_3_data);
    free(pAudio->channel_2_data);
    free(pAudio->channel_1_data);
    free(pAudio->read_data);
    snd_pcm_close(AUDIO_CARD_ID, PCM_IN);
    ac107_pdm_deinit();
}






OS_Queue_t data_queue;
volatile uint8_t is_wake_up = 0;
static void record_task(void *arg)
{
    int ret;
    struct audioContext aContext;
   // record_output_init();
    printf("start to record\n");
    ret = _audio_record_start(&aContext);
    if (ret) {
        goto err1;
    }

    printf("init success\n");

    while (1) {
        _audio_record_get_data(&aContext);
		if(is_wake_up ==0 && OS_QueueSend(&data_queue, aContext.channel_1_data, 20) != 0)
			printf("queue send  error\r\n");

    	}

    _audio_record_stop(&aContext);

err1:
   
exit:
    OS_ThreadDelete(&record_thread);
}



#include "am_tone_player.h"

void  model_process()
{

    float *outbuffer = malloc(43*sizeof(float));
	float *temp_buffer = malloc(43*40*sizeof(float));
	float *model_input= c_get_model_inputbuffer();
	printf("model_input addr:%08x", model_input);
	static int cnt =0;
	short *data_20ms = malloc(320*2);
	short *data_10ms = malloc(320);
	short *src_data_10ms = malloc(320);
	float predit_value = 0;
	
	am_tone_player_init();
	int sampleRate1 = 16000;
	
    int inMicLevel, outMicLevel = -1;
    uint8_t saturationWarning = 1;    
	int16_t echo = 0;
   while(1)
   	{
       if(OS_QueueReceive(&data_queue,src_data_10ms,20 ) != 0)
       	{
                printf("queue rev error\r\n");
				continue;
       	}
	   
	   if(cnt  == 0)
				{
				   memcpy(data_20ms,data_10ms, 160*sizeof(short));
				   cnt++;
			}
			else
				   memcpy(data_20ms+160, data_10ms, 160*sizeof(short)); 
			if(cnt >= 1)
				{
					get_spectrogram(data_20ms, outbuffer);
					memcpy(model_input+cnt*43, outbuffer, 43*4);
					if(cnt < 99)
						{
						  cnt++;
						}
					if(cnt == 99)
						{
							cnt = 40;
							memcpy(temp_buffer, model_input+43*(99-cnt), 43*cnt*4);
						    predit_value = c_get_predit()[0];
							printf("%f ", predit_value);
							if(predit_value > 0.97)
								{
								   printf(">>>>>>>>%f ", predit_value);
								   is_wake_up = 1;
								   while(OS_QueueReceive(&data_queue,src_data_10ms,20 ) == 0);
								   am_tone_player_defaultplay(WAKEUP_M);                         
								   is_wake_up = 0;
                                   cnt = 0;                                								   
								}
							else
								{
							       memcpy(model_input, temp_buffer, 43*cnt*4);
								}
						}
					memcpy(data_20ms, data_20ms+160, 160*sizeof(short));
					 
				}

      // OS_MSleep(1);
    }


}

int audio_record_start()
{

    OS_QueueCreate(&data_queue, 50, 320);
    if (OS_ThreadCreate(&record_thread,
                        "record_task",
                        record_task,
                        NULL,
                        OS_THREAD_PRIO_APP,
                        RECORDER_THREAD_STACK_SIZE) != OS_OK) {
        printf("thread create fail.exit\n");
        return -1;
    }

	 if (OS_ThreadCreate(&model_thread,
                        "model_thread",
                        model_process,
                        NULL,
                        OS_THREAD_PRIO_APP,
                        8*1024) != OS_OK) {
        printf("thread create fail.exit\n");
        return -1;
    }					
    return 0;
}

